package com.open.custom.api.controller;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.TimeInterval;
import cn.hutool.core.img.ImgUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjUtil;
import com.open.custom.api.config.properties.LocalFsProperties;
import com.open.custom.api.service.business.OssClient;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import net.coobird.thumbnailator.Thumbnails;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.CacheControl;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.math.BigDecimal;
import java.nio.file.FileSystem;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;


@RestController
@Slf4j
@RequestMapping(value = "/view/v1")
public class FileViewRestController {

    @Autowired
    private OssClient ossClient;

    @Resource
    private LocalFsProperties localFsProperties;


//    @GetMapping(value = "/img/{fileId}")
//    public ResponseEntity<byte[]> img(@PathVariable("fileId") String fileId,
//                                      @RequestParam(value = "scale", required = false) Float scale,
//                                      @RequestParam(value = "width", required = false) Integer width) throws Exception {
//        log.info("fileId:{} scale:{} width:{}", fileId, scale, width);
//        boolean hasFileType = fileId.contains(".");
//        byte[] bytes = ossClient.get(hasFileType ? fileId.substring(0, fileId.lastIndexOf(".")) : fileId);
//        MediaType mediaType = getMediaType(fileId);
//        mediaType = mediaType == null ? MediaType.IMAGE_JPEG : mediaType;
//
//        final TimeInterval timer = DateUtil.timer();
//        if (scale != null && !scale.equals(1F)) {
//            log.info("start scale");
//
//            try (final ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
//                 final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
//            ) {
//                ImgUtil.scale(inputStream, outputStream, scale);
//                bytes = outputStream.toByteArray();
//            } catch (Exception e) {
//                log.error("scale error", e);
//            }
//        } else if (width != null) {
//            log.info("start scale by width");
//            try (final ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
//                 final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
//            ) {
//                // 根据宽度计算缩放比例
//                final BufferedImage bufferedImage = ImgUtil.read(inputStream);
//                final int widthOrig = bufferedImage.getWidth();
//                final float scaleDest = (float) NumberUtil.div((float) width, widthOrig);
//                log.info("scale by width get scaleDest:{}", NumberUtil.roundDown(scaleDest, 2));
//                final Image image = ImgUtil.scale(bufferedImage, scaleDest);
//                ImgUtil.write(image, ImgUtil.IMAGE_TYPE_JPG, outputStream);
//                bytes = outputStream.toByteArray();
//            } catch (Exception e) {
//                log.error("scale by width error", e);
//            }
//        }
//        log.info("end scale dura:{}", timer.intervalPretty());
//        CacheControl cacheControl = CacheControl.maxAge(1, TimeUnit.MINUTES);
//        return ResponseEntity.ok()
//                // 缓存放在nginx做
////                .cacheControl(cacheControl)
//                .contentType(mediaType).body(bytes);
//    }


    @GetMapping(value = "/img/{fileId}")
    public ResponseEntity<byte[]> img(@PathVariable("fileId") String fileId,
                                      @RequestParam(value = "scale", required = false) Float scale,
                                      @RequestParam(value = "width", required = false) Integer width) throws Exception {
        log.info("fileId:{} scale:{} width:{}", fileId, scale, width);
        boolean hasFileType = fileId.contains(".");
        String ossId = hasFileType ? fileId.substring(0, fileId.lastIndexOf(".")) : fileId;

        // 获取文件类型
        MediaType mediaType = getMediaType(fileId, MediaType.APPLICATION_OCTET_STREAM);

        // 目前发现对图片进行缩放其实耗时有点多,所以采用把缩放后的图片缓存到本地,下次可以直接取
        String cacheFileName = "";
        if (ObjUtil.isNotEmpty(scale)) {
            cacheFileName = ossId + "_s" + scale;
        } else if (ObjUtil.isNotEmpty(width)) {
            cacheFileName = ossId + "_w" + width;
        } else {
            // 原图也缓存一下
            cacheFileName = ossId;
        }

        final File file = getCacheFile(cacheFileName);
        final boolean exist = FileUtil.exist(file);
        if (exist) {
            log.info("cache file exist, file:{}", file.getAbsolutePath());
            byte[] cacheBytes = FileUtil.readBytes(file);
            return ResponseEntity.ok().contentType(mediaType).body(cacheBytes);
        }


        // 获取和转换图片
        byte[] bytes = ossClient.get(ossId);
        byte[] bytesOut;
        if (scale != null || width != null) {
            // 需要对图片进行处理
            bytesOut = scaleImg(bytes, scale, width);
        } else {
            // 不需要对图片进行处理
            bytesOut = bytes;
        }

        // 开启异步线程把缓存写到本地
        ThreadUtil.execute(() -> {
            try {
                try (final FileOutputStream fileOutputStream = new FileOutputStream(file)) {
                    fileOutputStream.write(bytesOut);
                }
            } catch (Exception e) {
                log.error("cache error", e);
            }
        });


        return ResponseEntity.ok().contentType(mediaType).body(bytesOut);
    }


    private File getCacheFile(String cacheFileName) {
        final String cachePath = localFsProperties.getCachePath();
        return FileUtil.newFile(cachePath + File.separator + cacheFileName);
    }

    private byte[] scaleImg(byte[] bytes, Float scale, Integer width) {
        byte[] bytesOut;
        final TimeInterval timer = DateUtil.timer();

        log.info("start scaleImg");
        try (final ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
             final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ) {
            final Thumbnails.Builder<? extends InputStream> builder = Thumbnails.of(inputStream);
            if (scale != null) {
                builder.scale(scale);
            } else if (width != null) {
                builder.width(width);
                // 保持宽高比
                builder.keepAspectRatio(true);
            }
            builder.toOutputStream(outputStream);
            bytesOut = outputStream.toByteArray();
        } catch (Exception e) {
            log.error("scaleImg error", e);
            log.info("scaleImg error, use original bytes");
            bytesOut = bytes;
        }
        log.info("end scaleImg dura:{}", timer.intervalPretty());
        return bytesOut;
    }


//    private byte[] scaleImg(byte[] bytes, Float scale, Integer width) {
//        byte[] bytesOut = null;
//        final TimeInterval timer = DateUtil.timer();
//
//        // 统一缩放为返回20KB的照片
//        // 40Kb -> 20Kb   40*0.5=20
//        // 80Kb -> 20Kb   80*0.25=20
//        final int origKb = bytes.length / 1024;
//        scale = 200.0F / origKb;
//        // scale 保留2位小数
//        scale = new BigDecimal(scale).setScale(1, BigDecimal.ROUND_HALF_UP).floatValue();
//        if (scale <= 0.0F) {
//            scale = 0.1F;
//        }
//        log.info("scale:{}", scale);
//
//        if (scale >= 1) {
//            log.info("dont need scale");
//            return bytes;
//        }
//
//        log.info("start scaleImg");
//        try (final ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
//             final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
//        ) {
//            final Thumbnails.Builder<? extends InputStream> builder = Thumbnails.of(inputStream);
//            if (scale != null) {
//                builder.scale(scale);
//            } else if (width != null) {
//                builder.width(width);
//                // 保持宽高比
//                builder.keepAspectRatio(true);
//            }
//            builder.toOutputStream(outputStream);
//            bytesOut = outputStream.toByteArray();
//        } catch (Exception e) {
//            log.error("scaleImg error", e);
//        }
//        log.info("end scaleImg dura:{}", timer.intervalPretty());
//        return bytesOut;
//    }

    /**
     * 将字节数转换为易读的格式（GB、MB、KB）
     *
     * @param sizeInBytes 字节数
     * @return 转换后的字符串（如 "1GB20MB10KB"）
     */
    public static String formatSize(long sizeInBytes) {
        // 转换为 KB
        long sizeInKB = sizeInBytes / 1024;

        // 计算 GB、MB、KB
        long gb = sizeInKB / (1024 * 1024); // 1 GB = 1024 MB = 1024 * 1024 KB
        long mb = (sizeInKB % (1024 * 1024)) / 1024; // 剩余的 MB
        long kb = sizeInKB % 1024; // 剩余的 KB

        // 拼接结果
        StringBuilder result = new StringBuilder();
        if (gb > 0) {
            result.append(gb).append("GB");
        }
        if (mb > 0) {
            result.append(mb).append("MB");
        }
        if (kb > 0) {
            result.append(kb).append("KB");
        }

        // 如果结果为空白，说明大小小于 1KB，返回 0KB
        if (result.length() == 0) {
            return "0KB";
        }

        return result.toString();
    }

    @GetMapping(value = "/auto/{fileId}")
    public ResponseEntity<byte[]> auto(@PathVariable("fileId") String fileId) throws Exception {
        boolean hasFileType = fileId.contains(".");
        final byte[] bytes = ossClient.get(hasFileType ? fileId.substring(0, fileId.lastIndexOf(".")) : fileId);
        MediaType mediaType = getMediaType(fileId, MediaType.APPLICATION_OCTET_STREAM);
        final ResponseEntity.BodyBuilder bodyBuilder = ResponseEntity.ok()
                .contentType(mediaType);

        final String type = mediaType.getType();
        Set<String> canViewType = new HashSet<>();
        canViewType.add("image");
        canViewType.add("audio");
        canViewType.add("video");
        if (canViewType.contains(type)) {
            bodyBuilder.header("Content-Disposition", "inline; filename=" + fileId);
        } else {
            bodyBuilder.header("Content-Disposition", "attachment; filename=" + fileId);
        }
        return bodyBuilder.body(bytes);
    }

    private MediaType getMediaType(String fileId, MediaType defaultType) {
        MediaType mediaType = getMediaType(fileId);
        mediaType = mediaType == null ? defaultType : mediaType;
        return mediaType;
    }

    private MediaType getMediaType(String fileId) {
        boolean hasFileType = fileId.contains(".");
        if (!hasFileType) {
            return null;
        }
        MediaType mediaType = null;
        try {
            final String mimeType = FileUtil.getMimeType(fileId);
            if (ObjUtil.isNotEmpty(mimeType)) {
                mediaType = MediaType.parseMediaType(mimeType);
            }
        } catch (Exception e) {
            log.error("getMimeType", e);
        }
        return mediaType;
    }


}
